package com.hotacorp.opencloud.authserver.config;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.CompositeTokenGranter;
import org.springframework.security.oauth2.provider.OAuth2RequestFactory;
import org.springframework.security.oauth2.provider.TokenGranter;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;

import com.hotacorp.opencloud.authserver.bean.SysUserDetail;
import com.hotacorp.opencloud.authserver.exception.CustomWebResponseExceptionTranslator;
import com.hotacorp.opencloud.authserver.granter.PhoneSMSTokenGranter;
import com.hotacorp.opencloud.authserver.granter.UserNamePassWordCodeTokenGranter;
import com.hotacorp.opencloud.authserver.service.impl.ClientDetailsServiceImpl;
import com.hotacorp.opencloud.authserver.service.impl.UserDetailsServiceImpl;
import com.hotacorp.opencloud.common.commdata.Constants;

/**
 * OAuth2授权服务设置
 * 1、设置token store为reids
 * 2、设置token包含附加信息
 * 3、加入手机号、短信验证码的登录模式
 * 4、加入用户名、密码、图片验证码的登录模式
 * @author lwg
 *
 */
@Configuration
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
	@Autowired
	private AuthenticationManager authenticationManager;
	@Autowired
	private RedisConnectionFactory connectionFactory;
	@Autowired
	StringRedisTemplate stringRedisTemplate;
	@Autowired
	private UserDetailsServiceImpl enhanceUserDetailsService;
	@Autowired
	private ClientDetailsServiceImpl clientDetailsService;

	@Bean
	public TokenStore getTokenStore() {
		RedisTokenStore tokenStore = new RedisTokenStore(connectionFactory);
		tokenStore.setPrefix(Constants.TOKEN_PRE + ":");
		return tokenStore;
	}

	@Bean
	public PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}

	@Override
	public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
		clients.withClientDetails(clientDetailsService);
	}

	@Override
	public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
		endpoints
		.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST)
		.tokenStore(getTokenStore())
		.tokenEnhancer(tokenEnhancer())
		.userDetailsService(enhanceUserDetailsService)
		.authenticationManager(authenticationManager)
		.reuseRefreshTokens(false);
		List<TokenGranter> tokenGranters = getTokenGranters(endpoints.getTokenServices(),
				endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory());
		tokenGranters.add(endpoints.getTokenGranter());
		endpoints.tokenGranter(new CompositeTokenGranter(tokenGranters));
		endpoints.exceptionTranslator(new CustomWebResponseExceptionTranslator());
	}

	@Override
	public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
		// 允许表单认证
		security.allowFormAuthenticationForClients().tokenKeyAccess("permitAll()").checkTokenAccess("permitAll()");
        //security.authenticationEntryPoint(new AuthExceptionEntryPoint());
	}

	private List<TokenGranter> getTokenGranters(AuthorizationServerTokenServices tokenServices,
			ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory) {
		return new ArrayList<>(Arrays.asList(
				new PhoneSMSTokenGranter(tokenServices, clientDetailsService, requestFactory, enhanceUserDetailsService,
						stringRedisTemplate),
				new UserNamePassWordCodeTokenGranter(authenticationManager, tokenServices, clientDetailsService, requestFactory, stringRedisTemplate)));
	}

	/**
	 * 使token的信息里包含userid,tenant_id(租户ID),dept_id,nick_name,user_phone信息
	 * @return
	 */
	@Bean
	public TokenEnhancer tokenEnhancer() {
		return (accessToken, authentication) -> {
			final Map<String, Object> additionalInfo = new HashMap<>(4);
			SysUserDetail user = (SysUserDetail) authentication.getUserAuthentication().getPrincipal();
			additionalInfo.put(Constants.USER_ID, user.getUserid());
			additionalInfo.put(Constants.TENANT_ID, user.getTenantid());
			additionalInfo.put(Constants.DEPT_ID, user.getDepid());
			additionalInfo.put(Constants.NICK_NAME, user.getNickname());
			additionalInfo.put(Constants.USER_PHONE, user.getPhone());
			((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
			return accessToken;
		};
	}
}
